<?php

class JdidClient {

    public $serverUrl = "https://open.jd.id/api";
    public $accessToken;
    public $connectTimeout = 0;
    public $readTimeout = 0;
    public $appKey;
    public $appSecret;
    public $version = "1.0";
    public $format = "json";
    public $signMethod = "md5";
//    private $charset_utf8 = "UTF-8";
    public $method;
    public $param_json;
    public $param_file;
    public $debugMode = false;

    protected function generateSign($params) {
        ksort($params);
        $stringToBeSigned = $this->appSecret;
        foreach ($params as $k => $v) {
            if ("@" != substr($v, 0, 1)) {
                $stringToBeSigned .= "$k$v";
            }
        }
        unset($k, $v);
        $stringToBeSigned .= $this->appSecret;

        //online env should comment this print
        //print("before sign raw: ".$stringToBeSigned."\n");
        return strtoupper(md5($stringToBeSigned));
    }

    public function curl($url, $postFields = null) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if ($this->readTimeout) {
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->readTimeout);
        }
        if ($this->connectTimeout) {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
        }
        //https request
        if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        if (is_array($postFields) && 0 < count($postFields)) {
            $postBodyString = "";
            $postMultipart = false;
            foreach ($postFields as $k => $v) {
                //if("@" != substr($v, 0, 1))//check whether file upload or not
                if ($k != 'param_file') {
                    $postBodyString .= "$k=" . urlencode($v) . "&";
                } else {// for file upload, set to multipart/form-data£¬else set to www-form-urlencoded
                    $postMultipart = true;
                }
            }
            unset($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                //There is a new Variable included with curl in PHP 5.5: 
                //CURLOPT_SAFE_UPLOAD this is set to false by default in PHP 5.5 and is switched to a default of true in PHP 5.6.
                curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
            } else {
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }
        $reponse = curl_exec($ch);

        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new Exception($reponse, $httpStatusCode);
            }
        }
        curl_close($ch);
        return $reponse;
    }

    public function getStandardOffsetUTC($timezone) {
        if ($timezone == 'UTC') {
            return '+0000';
        } else {
            $timezone = new DateTimeZone($timezone);
            $transitions = array_slice($timezone->getTransitions(), -3, null, true);

            foreach (array_reverse($transitions, true) as $transition) {
                if ($transition['isdst'] == 1) {
                    continue;
                }

                return sprintf('%+03d%02u', $transition['offset'] / 3600, abs($transition['offset']) % 3600 / 60);
            }

            return false;
        }
    }

    public function getCurrentTimeFormatted() {
        return date("Y-m-d H:i:s") . '.000' . $this->getStandardOffsetUTC(date_default_timezone_get());
    }

    public function call() {
        //construct system parameters
        $sysParams["app_key"] = $this->appKey;
        $sysParams["v"] = $this->version;
        $sysParams["format"] = $this->format;
        $sysParams["sign_method"] = $this->signMethod;
        $sysParams["method"] = $this->method;
        $sysParams["timestamp"] = $this->getCurrentTimeFormatted();
        $sysParams["access_token"] = $this->accessToken;


        //get business parameters
        if (null != $this->param_json && isset($this->param_json) && !empty($this->param_json)) {
            $sysParams["param_json"] = $this->param_json;
        } else {
            $sysParams["param_json"] = "{}";
        }


        //sign
        $sysParams["sign"] = $this->generateSign($sysParams);
        //system parameter put into POST request string
        $requestUrl = $this->serverUrl;
        //$requestUrl = $this->serverUrl . "?";

        $arr4Post = array();
        foreach ($sysParams as $sysParamKey => $sysParamValue) {
            $arr4Post["$sysParamKey"] = $sysParamValue;
        }
        //send HTTP request
        try {
            $resp = $this->curl($requestUrl, $arr4Post);

            // debug info start //
            if ($this->debugMode) {
                echo '----- api call -----', PHP_EOL;
                echo 'url: ', $requestUrl, PHP_EOL;
                echo 'method: ', $this->method, PHP_EOL;
                echo 'param: ', $this->param_json, PHP_EOL;
                echo 'arr4post: ', http_build_query($arr4Post), PHP_EOL, PHP_EOL;
                echo 'resp: ', $resp, PHP_EOL, PHP_EOL;
            }
            // debug info end //
            //print("\nfunction call resp:\n".$resp);
            return $resp;
        } catch (Exception $e) {
            print("\nfunction call error happened.\n" . $e);
            $result->openapi_code = $e->getCode();
            $result->openapi_msg = $e->getMessage();
            return $result;
        }
    }

    public function call4BigData() {
        //construct system parameters
        $sysParams["app_key"] = $this->appKey;
        $sysParams["v"] = $this->version;
        $sysParams["format"] = $this->format;
        $sysParams["sign_method"] = $this->signMethod;
        $sysParams["method"] = $this->method;
        $sysParams["timestamp"] = $this->getCurrentTimeFormatted();
        $sysParams["access_token"] = $this->accessToken;


        //get business parameters
        if (null != $this->param_json && isset($this->param_json) && !empty($this->param_json)) {
            $sysParams["param_json"] = $this->param_json;
        } else {
            $sysParams["param_json"] = "{}";
        }
        //get business file which would upload
        if (null != $this->param_file && isset($this->param_file) && !empty($this->param_file)) {
            $sysParams["param_file_md5"] = strtoupper(md5_file($this->param_file));
        }

        //do sign
        $sysParams["sign"] = $this->generateSign($sysParams);
        //
        $requestUrl = $this->serverUrl;
        //curl function will check '@' flag to upload file using  multipart/form-data
        //curl function will check '@' flag to upload file using  multipart/form-data
        //$arr4File = array("param_file" => "@" . $this->param_file); 
        $arr4File = array("param_file" => new \CURLFile($this->param_file));
        foreach ($sysParams as $sysParamKey => $sysParamValue) {
            $arr4File["$sysParamKey"] = $sysParamValue;
        }

        //do http request
        try {
            $resp = $this->curl($requestUrl, $arr4File);
            //print("\nfunction call resp:\n".$resp);
            return $resp;
        } catch (Exception $e) {
            print("\nfunction call4BigData error happened.\n" . $e);
            $result->openapi_code = $e->getCode();
            $result->openapi_msg = $e->getMessage();
            return $result;
        }
    }
}
